<%@ page language="java" pageEncoding="UTF-8" contentType="text/html;charset=utf-8" %>
<%@ taglib uri="http://java.sun.com/jsp/jstl/core" prefix="c" %>
<%@ taglib uri="http://java.sun.com/jsp/jstl/fmt" prefix="fmt" %>
<%@ taglib prefix="fn" uri="http://java.sun.com/jsp/jstl/fmt" %>
<c:set var="ctx" value="${pageContext.request.contextPath}"/>
<style>
    .log-container {
        padding: 15px;
    }

    .fix::after {
        clear: both;
        content: " ";
        display: block;
        font-size: 0;
        height: 0;
        visibility: hidden;
    }

    .log-container .log-box {
        margin: 0 auto;
        border-radius: 5px !important;
        box-shadow: 0 0 4px 1px #DEDEDE;
    }

    .log-box .panel-heading {
        padding: 3px 6px;
    }

    .log-container .log-box .log-control span {
        cursor: pointer;
        margin: 0 5px;
    }

    .log-container .log-box .log-control .log-filter {
        width: 500px;
        float: left;
    }

    .log-control .log-input,
    .log-control .log-input,
    .log-control .log-input {
        width: 150px !important;
        float: left !important;
        margin-left: 5px !important;
        border-radius: 5px !important;
        font-size: 12px !important;
    }

    .log-control .log-filter .form-control {
        background: transparent !important;
        height: auto !important;
    }

    .log-control .log-filter .btn {
        border-radius: 5px !important;
        font-size: 12px !important;
    }

    .log-container .log-box .log-control .log-keys {
        float: right;
        line-height: 32px;
        vertical-align: middle;
        text-align: right;
    }

    .log-container .log-box .log-control .log-keys .log-range {
        width: 80px !important;
        height: 30px !important;
    }

    .log-box .log-content {
        height: 400px;
        overflow-x: auto;
        overflow-y: auto;
        padding: 10px 15px;
        background: #2A2A2A;
        color: #B9B9B9;
    }

    .log-content .log-item {
        white-space: nowrap;
        font-size: 15px;
        font-weight: normal;
        opacity: 0.8;
    }

    .log-content .log-item p {
        padding: 1px;
    }

    .log-content .log-item .seq {
        min-width: 15px;
        display: inline-block;
    }

    .log-content .log-item:hover p {
        opacity: 1;
        cursor: pointer;
    }

    .log-content .log-item:hover p .seq {
        color: #E64331;
    }


    .log-item span {
        margin: 0 3px;
    }

    .log-item .level {
        color: #D1BF60;
    }

    .log-item .className {
        color: #2D958E;
    }

    .log-item .username {
        color: #2798C6;
    }

    .log-item .log-exception {
        padding-left: 10px;
    }

    .log-item .log-exception .stackTrace-message,
    .log-item .log-exception .stackTrace-item {
        display: block;
        vertical-align: middle;
    }

</style>
<div class="main">
    <div class="page-header">
        <h3>
            <span>运行日志</span>
        </h3>
    </div>


    <div id="logContainer" class="log-container">
        <div class="panel panel-default log-box">
            <div class="panel-heading">
                <div class="log-control fix">
                    <div id="logFilter" class="log-filter fix">
                        <input id="keywords" class="keywords form-control log-input" placeholder="关键词过滤"/>
                        <select id="userSelector" class="userSelector selectpicker show-tick form-control log-input"
                                multiple
                                data-live-search="true" autocomplete="off">
                            <option value="anonymous">anonymous</option>
                            <c:forEach items="${userList}" var="user">
                                <option value="${user.username}" ${currentUser.username == user.username ? "selected" : ""}>${user.username}</option>
                            </c:forEach>
                        </select>
                        <select id="levelSelector" class="levelSelector selectpicker show-tick form-control log-input"
                                multiple
                                data-live-search="true" autocomplete="off">
                            <option value="TRACE">TRACE</option>
                            <option value="DEBUG" selected>DEBUG</option>
                            <option value="INFO">INFO</option>
                            <option value="WARN">WARN</option>
                            <option value="ERROR">ERROR</option>
                        </select>
                    </div>
                    <div id="logKeys" class="log-keys fix">
                        <input id="logRange" class="log-range form-control log-input" placeholder="1-X"/>
                        <span id="logCatalina" class="log-catalina"><i class="glyphicon glyphicon-play"></i></span>
                        <span id="logCopy" class="log-copy" title="点击复制"><i class="glyphicon glyphicon-copy"></i></span>
                        <span id="logClear" class="log-clear"><i class="glyphicon glyphicon-trash"></i></span>
                    </div>
                </div>
            </div>
            <div class="log-content">
                <ul>

                </ul>
            </div>
        </div>


    </div>
</div>
<script type="text/javascript">
    require(['apiUtil', 'bootbox', 'scripts/socket/sockjs.min', 'scripts/socket/stomp', 'scripts/socket/clipboard.min'],
        function (apiUtil, bootbox, SockJS, stomp, ClipboardJS) {
            window.bootbox = bootbox;
            window.apiUtil = apiUtil;

            //初始化控件
            $('#userSelector').selectpicker({noneSelectedText: "请选择用户"});
            $('#levelSelector').selectpicker({noneSelectedText: "请选择日志级别"});

            //日志容器
            var logContainerJQ = $("#logContainer");
            var logContentJQ = logContainerJQ.find(".log-content>ul");

            //日志子项模板
            var logTemplate = '<li class="log-item" log-username="{{username}}" log-level="{{level}}" log-seq="{{seq}}">' +
                '<p class="log-message">' +
                '<span class="seq">{{seq}}</span>' +
                '<span class="username">{{username}}</span>' +
                '<span class="logTime">{{logTime}}</span>' +
                '<span class="threadName">{{threadName}}</span>' +
                '<span class="clientIp">{{clientIp}}</span>' +
                '<span class="level">{{level}}</span>' +
                '<span class="className">{{className}}</span>' +
                '<span class="methodName">{{methodName}}:{{lineNumber}}</span>' +
                '<span class="message">- {{message}}</span>' +
                '</p>{{exceptionStr}}</li>';

            //websocket
            var socket = new SockJS("${ctx}/sock");
            var stompClient = Stomp.over(socket);

            //连接服务器
            stompClient.connect({}, function (frame) {
                logContentJQ.append('<li class="log-item">连接建立成功,请点击按键接收日志...</li>')

                //连接成功后,为logControl绑定事件
                var logCatalinaJQ = $("#logCatalina");

                //点击订阅或取消订阅日志
                logCatalinaJQ.on("click", function () {
                    var _this = this;
                    var iconJQ = $(_this).find("i.glyphicon");

                    var hasStartClass = iconJQ.hasClass("glyphicon-play");
                    if (hasStartClass) {
                        //订阅日志
                        _this.subscription = subscribeLog(stompClient, iconJQ, logContentJQ);
                    } else {
                        //取消订阅日志
                        unsubscribeLog(_this.subscription, iconJQ, logContentJQ);
                    }
                });

            }, function (error) {
                logContentJQ.append('连接失败【' + error + '】');
            });


            //订阅日志信息
            var seq = 0;

            function subscribeLog(stompClient, iconJQ, logContentJQ) {

                //公共订阅,服务器可以通过该地址发送信息给所有用户
                var subscription = stompClient.subscribe("/topic/runningLog", function (res) {
                    if (res && res.body) {
                        var logMessage = JSON.parse(res.body);

                        //判断是否满足过滤信息,默认为满足
                        //获取过滤选项的值
                        var userArray = $('#userSelector').selectpicker("val");
                        var levelArray = $('#levelSelector').selectpicker("val");
                        var keywords = $('#keywords').val();
                        var correct = true;
                        if (null != userArray && userArray.length > 0) {
                            correct = $.inArray(logMessage.username, userArray) >= 0;
                        }

                        if (null != levelArray && levelArray.length > 0) {
                            correct = correct && $.inArray(logMessage.level, levelArray) >= 0;
                        }

                        if (null != keywords && $.trim(keywords).length > 0) {
                            correct = correct && (logMessage.clientIp.indexOf(keywords) >= 0 || logMessage.threadName.indexOf(keywords) >= 0
                                || logMessage.className.indexOf(keywords) >= 0 || logMessage.methodName.indexOf(keywords) >= 0
                                || logMessage.message.indexOf(keywords) >= 0
                                || logMessage.logTime.indexOf(keywords) >= 0
                                || (logMessage.exceptionStr ? logMessage.exceptionStr.indexOf(keywords) >= 0 : false));
                        }

                        if (correct) {
                            seq = seq + 1;
                            var logContent = logTemplate.replace(/\{\{seq\}\}/g, seq)
                                .replace(/\{\{username\}\}/g, logMessage.username)
                                .replace("{{logTime}}", logMessage.logTime)
                                .replace("{{threadName}}", logMessage.threadName)
                                .replace("{{clientIp}}", logMessage.clientIp)
                                .replace(/\{\{level\}\}/g, logMessage.level)
                                .replace("{{className}}", logMessage.className)
                                .replace("{{methodName}}", logMessage.methodName)
                                .replace("{{lineNumber}}", logMessage.lineNumber)
                                .replace("{{message}}", logMessage.message)
                                .replace("{{exceptionStr}}", logMessage.exceptionStr ? ('<div class="log-exception">' + logMessage.exceptionStr + '</div>') : "");
                            logContentJQ.append(logContent);
                        }
                    }
                });
                iconJQ.removeClass("glyphicon-play").addClass("glyphicon-stop");
                logContentJQ.append('<li class="log-item">开始接收日志...</li>');
                return subscription;
            }

            //取消订阅日志信息
            function unsubscribeLog(subscription, iconJQ, logContentJQ) {
                subscription.unsubscribe();
                iconJQ.removeClass("glyphicon-stop").addClass("glyphicon-play");
                logContentJQ.append('<li class="log-item">停止接收日志...</li>')
            }

            //清理日志
            $("#logClear").on("click", function () {
                logContentJQ.html("");
            });

            //点击复制内容
            var cb = new ClipboardJS('.log-copy', {
                text: function (trigger) {
                    //获取行号
                    var lineNumberBegin = 1;
                    var lineNumberEnd = null;
                    var logRangeStr = $("#logRange").val();
                    var logRangeArray;
                    if (null == logRangeStr || logRangeStr.length === 0) {
                        logRangeArray = [];
                    } else {
                        logRangeArray = $.trim(logRangeStr).split("-");
                    }

                    //输入参数包含起止行号则进行赋值,否则使用不进行限制
                    if (logRangeArray.length === 1) {
                        try {
                            lineNumberBegin = parseInt(logRangeArray[0]);
                        } catch (e) {
                            alert("行号无效");
                            return "";
                        }
                    }

                    if (logRangeArray.length === 2) {
                        try {
                            lineNumberBegin = parseInt(logRangeArray[0]);
                            lineNumberEnd = parseInt(logRangeArray[1]);
                        } catch (e) {
                            alert("行号无效");
                            return "";
                        }
                    }

                    var copyTextArray = [];
                    logContentJQ.find(".log-item").each(function (index, element) {
                        var eleJQ = $(element);
                        var logSeq = eleJQ.attr("log-seq");
                        var write = false;
                        if (logSeq >= lineNumberBegin) {
                            write = lineNumberEnd ? (logSeq <= lineNumberEnd) : true;
                        }

                        //如果符合要求则写入
                        if (write) {
                            var text = "";
                            eleJQ.find("span").each(function (i, e) {
                                text = text + $(e).text() + " ";
                            });
                            copyTextArray.push(text);
                        }
                    });
                    return copyTextArray.join("\n");
                }
            });

            cb.on('success', function (e) {
                alert("复制成功!");
                e.clearSelection();
            });

            cb.on('error', function (e) {
                alert("复制失败,请重试或者您的浏览器可能不支持该操作!")
            });


            //页面初始化
            changeLogContentHeight();

            //监听浏览器窗口,动态调整控制台的高度
            $(window).resize(function () {
                changeLogContentHeight();
            });

            //根据窗口高度和可视元素调整logContent的高度
            function changeLogContentHeight() {
                //获取浏览器显示区域的高度及navbar、pageHeader的高度
                var windowHeight = $(window).height();
                var navbarHeight = $("#navbar").outerHeight(true);
                navbarHeight = !navbarHeight ? 60 : navbarHeight;
                var pageHeaderHeight = $(".main>.page-header").outerHeight(true);
                pageHeaderHeight = !pageHeaderHeight ? 50 : pageHeaderHeight;
                var panelHeadingHeight = $("#logContainer .panel-heading").outerHeight(true);
                panelHeadingHeight = !panelHeadingHeight ? 40 : panelHeadingHeight;

                //logContainer和logContent的填充距
                var paddingHeight = 15 * 2 + 10 * 2;

                //容错值
                var faultTolerant = 50;
                var logContentHeight = windowHeight - navbarHeight - pageHeaderHeight - panelHeadingHeight - paddingHeight - faultTolerant;

                //设置日志内容高度
                $("#logContainer .log-content").height(logContentHeight);
            }
        });
</script>


